﻿#include "pinyinhelper.h"

#include <set>
#include <list>
#include <vector>
#include <map>
#include <algorithm>

//var
static  bool gInit = false;
static  std::vector<std::wstring> gDict;
static  std::map<std::wstring, std::wstring> gPinyinTable;
static  std::map<std::wstring, std::wstring> gMutilPinyinTable;
static  DoubleArrayTrie gDoubleArrayTrie;
static  std::wstring gPinyinSeparator = L","; // 拼音分隔符
const static  wchar_t gChineseLing = L'〇';
const static  std::wstring gAllUnmarkedVowel = L"aeiouv";
const static  std::wstring gAllMarkedVowel = L"āáǎàēéěèīíǐìōóǒòūúǔùǖǘǚǜ"; // 所有带声调的拼音字母

//fun
extern void replace(std::wstring &strBase, std::wstring strSrc, std::wstring strDes);
extern void split(std::wstring& s, std::wstring& delim,std::vector< std::wstring >* ret);
extern std::wstring toUpper(const std::wstring &data);

PinyinHelper::PinyinHelper() {
}

PinyinHelper::~PinyinHelper(){
}


bool caseInsensitiveLessThan(const std::wstring& s1, const std::wstring& s2) {
    std::wstring o1 = toUpper(s1);
    std::wstring o2 = toUpper(s2);
    return o1 < o2;
}

void PinyinHelper::initPinYinHelper() {
    if(gInit == false) {
        gPinyinTable = PinyinResource::getPinyinResource();
        gMutilPinyinTable = PinyinResource::getMutilPinyinResource();
        auto it = gMutilPinyinTable.begin();
        for(;it!=gMutilPinyinTable.end();++it) {
               gDict.push_back(it->first);
        }
        std::sort(gDict.begin(),gDict.end(),caseInsensitiveLessThan);
        gDoubleArrayTrie.build(gDict);
        gInit = true;
    }
}

std::vector<std::wstring> PinyinHelper::convertWithToneNumber(std::wstring pinyinArrayString) {
    std::vector<std::wstring> pinyinArray;
    split(pinyinArrayString,gPinyinSeparator,&pinyinArray);

    for (int i = pinyinArray.size() - 1; i >= 0; i--) {
        bool hasMarkedChar = false;

        std::wstring originalPinyin = pinyinArray[i];
        replace(originalPinyin,L"ü", L"v"); // 将拼音中的ü替换为v

        for (int j = originalPinyin.length() - 1; j >= 0; j--) {
            const wchar_t originalChar = originalPinyin.at(j);

            // 搜索带声调的拼音字母，如果存在则替换为对应不带声调的英文字母
            if (originalChar < L'a' || originalChar > L'z') {
                int indexInAllMarked = gAllMarkedVowel.find(originalChar);
                int toneNumber = indexInAllMarked % 4 + 1; // 声调数

                wchar_t replaceChar = gAllUnmarkedVowel.at((indexInAllMarked - indexInAllMarked % 4) / 4);

                replace(originalPinyin, std::wstring(1,originalChar), std::wstring(1,replaceChar));
                pinyinArray[i] = originalPinyin + std::to_wstring(toneNumber);

                hasMarkedChar = true;
                break;
            }
        }
        if (!hasMarkedChar) {
            // 找不到带声调的拼音字母说明是轻声，用数字5表示
            pinyinArray[i] = originalPinyin + L"5";
        }
    }

    return pinyinArray;
}

std::vector<std::wstring> PinyinHelper::convertWithoutTone(std::wstring pinyinArrayString) {
    std::vector<std::wstring> pinyinArray;

    for (int i = gAllMarkedVowel.length() - 1; i >= 0; i--) {
        const wchar_t originalChar = gAllMarkedVowel.at(i);
        const wchar_t replaceChar = gAllUnmarkedVowel.at((i - i % 4) / 4);
        replace(pinyinArrayString, std::wstring(1,originalChar), std::wstring(1,replaceChar));
    }
    // 将拼音中的ü替换为v
    replace(pinyinArrayString, L"ü", L"v");
    split(pinyinArrayString, gPinyinSeparator,&pinyinArray);
    return pinyinArray;
}

std::vector<std::wstring> PinyinHelper::formatPinyin(std::wstring pinyinString, PinyinFormat pinyinFormat) {
    if (pinyinFormat == PinyinFormat::WITH_TONE_MARK) {
        std::vector< std::wstring > pinyinArray;
        split(pinyinString, gPinyinSeparator,&pinyinArray);
        return pinyinArray;
    } else if (pinyinFormat == PinyinFormat::WITH_TONE_NUMBER) {
        return convertWithToneNumber(pinyinString);
    } else if (pinyinFormat == PinyinFormat::WITHOUT_TONE) {
        return convertWithoutTone(pinyinString);
    }
    return std::vector<std::wstring>();
}

std::vector<std::wstring> PinyinHelper::convertToPinyinArray(wchar_t c, PinyinFormat pinyinFormat) {
    std::wstring pinyin = gPinyinTable.at(std::wstring(1,c));
    if (!pinyin.empty()) {
        return formatPinyin(pinyin, pinyinFormat);
    }
    return std::vector<std::wstring>();
}

std::vector<std::wstring> PinyinHelper::convertToPinyinArray(wchar_t c) {
    return convertToPinyinArray(c, PinyinFormat::WITH_TONE_MARK);
}

std::wstring PinyinHelper::convertToPinyinString(std::wstring str, std::wstring separator, PinyinFormat pinyinFormat) {
    str = ChineseHelper::convertToSimplifiedChinese(str);
    std::wstring sb;
    int i = 0;
    int strLen = str.length();
    while (i < strLen) {
        std::wstring substr = str.substr(i);
        std::vector<int> commonPrefixList = gDoubleArrayTrie.commonPrefixSearch(substr);
        if (commonPrefixList.size() == 0) {
            wchar_t c = str.at(i);
            // 判断是否为汉字或者〇
            if (ChineseHelper::isChinese(c) || c == gChineseLing) {
                std::vector<std::wstring> pinyinArray = convertToPinyinArray(c, pinyinFormat);
                if (pinyinArray.size() != 0) {
                    sb += pinyinArray[0];
                } else {
                    sb += str.at(i);
                }
            } else {
                sb += c;
            }
            i ++;
        } else {
            std::wstring words = gDict.at(commonPrefixList.at(commonPrefixList.size() - 1));
            std::vector<std::wstring> pinyinArray = formatPinyin(gMutilPinyinTable.at(words), pinyinFormat);
            for (int j = 0, l = pinyinArray.size(); j < l; j++) {
                sb += pinyinArray[j];
                if (j < l - 1) {
                    sb += separator;
                }
            }
            i += words.length();
        }

        if (i < strLen) {
            sb += separator;
        }
    }
    return sb;
}

std::wstring PinyinHelper::convertToPinyinString(std::wstring str, std::wstring separator) {
    return convertToPinyinString(str, separator, PinyinFormat::WITH_TONE_MARK);
}

bool PinyinHelper::hasMultiPinyin(wchar_t c) {
    std::vector<std::wstring> pinyinArray = convertToPinyinArray(c);
    if (pinyinArray.size()> 1) {
        return true;
    }
    return false;
}

std::wstring PinyinHelper::getShortPinyin(std::wstring str) {
    std::wstring separator = L"#"; // 使用#作为拼音分隔符
    std::wstring sb;
    wchar_t *charArray = new wchar_t[str.length() + 1 ];
    charArray[str.length()] = 0;
    for (int i = 0, len = str.length(); i < len; i += 2) {
        wchar_t c = str.at(i);

        // 首先判断是否为汉字或者〇，不是的话直接将该字符返回
        if (!ChineseHelper::isChinese(c) && c != gChineseLing) {
            charArray[i] = c;
        } else {
            int j = i + 1;
            sb += c;

            // 搜索连续的汉字字符串
            while (j < len && (ChineseHelper::isChinese(str.at(j)) || str.at(j) == gChineseLing)) {
                sb += str.at(j);
                j++;
            }
            std::wstring hanziPinyin = convertToPinyinString(sb, separator, PinyinFormat::WITHOUT_TONE);
            std::vector<std::wstring> pinyinArray;
            split(hanziPinyin, separator,&pinyinArray);
            for (std::wstring string : pinyinArray) {
                charArray[i] = string.at(0);
                i++;
            }
            i--;
            sb.clear();
        }
    }
    return std::wstring(charArray);
}



